package com.raft.core.node.task;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.concurrent.ThreadSafe;

import com.raft.core.node.NodeId;
import com.raft.core.rpc.message.AppendEntriesResultMessage;
import com.raft.core.rpc.message.InstallSnapshotResultMessage;

/**经过基本校验(当前节点角色，加入节点的节点名，是否已经在catch-up中)之后，开始catch-up过程。理论上catch-up过程允许并行，所以要求NewNodeCatchUpTaskGroup
 * <br/>是一个线程安全的类。同时，catch-up过程中，通信组件收到的消息要转发给catch-up任务
 * Group for {@link NewNodeCatchUpTask}.
 */
@ThreadSafe
public class NewNodeCatchUpTaskGroup {

    private final ConcurrentMap<NodeId, NewNodeCatchUpTask> taskMap = new ConcurrentHashMap<>();

    /**追加任务，如果已经存在，返回false
     * <br/>Add task.
     *
     * @param task task
     * @return true if successfully, false if task for same node exists
     */
    public boolean add(NewNodeCatchUpTask task) {
        return taskMap.putIfAbsent(task.getNodeId(), task) == null;
    }

    /**确认是否是catch-up中的节点，如果是，转发到catch-up任务
     * <br/>Invoke <code>onReceiveAppendEntriesResult</code> on task.
     *
     * @param resultMessage result message
     * @param nextLogIndex  next index of log
     * @return true if invoked, false if no task for node
     */
    public boolean onReceiveAppendEntriesResult(AppendEntriesResultMessage resultMessage, int nextLogIndex) {
        NewNodeCatchUpTask task = taskMap.get(resultMessage.getSourceNodeId());
        if (task == null) {
            return false;
        }
        task.onReceiveAppendEntriesResult(resultMessage, nextLogIndex);
        return true;
    }

    /**确认是否是catch-up中的节点，如果是，转发到catch-up任务
     * @param resultMessage
     * @param nextLogIndex
     * @return
     */
    public boolean onReceiveInstallSnapshotResult(InstallSnapshotResultMessage resultMessage, int nextLogIndex) {
        NewNodeCatchUpTask task = taskMap.get(resultMessage.getSourceNodeId());
        if (task == null) {
            return false;
        }
        task.onReceiveInstallSnapshotResult(resultMessage, nextLogIndex);
        return true;
    }

    /**移除任务
     * <br/>Remove task.
     *
     * @param task task
     * @return {@code true} if removed, {@code false} if not found
     */
    public boolean remove(NewNodeCatchUpTask task) {
        return taskMap.remove(task.getNodeId()) != null;
    }

}
